package taishan

import (
	"bytes"
	"context"
	"fmt"
	"gitee.com/tiger000/taishan_web/utils/bufferpool"
	"gitee.com/tiger000/taishan_web/utils/conv"
	"gitee.com/tiger000/taishan_web/utils/ctx_parser"
	"gitee.com/tiger000/taishan_web/utils/httputils/utils"
	jsoniter "github.com/json-iterator/go"
	"github.com/spf13/cast"
	"go.uber.org/zap/buffer"
	"io"
	"math"
	"net/http"
	"strings"
)

type Context struct {
	server *Engine //服务器

	// origin objects
	writer   http.ResponseWriter
	Request  *http.Request
	Response *http.Response
	// request info
	Path   string
	Method string

	reqCopyBody []byte

	// response info
	StatusCode      int
	RespHeader      http.Header
	responseUpdated bool
	BodyBuffer      *buffer.Buffer

	routeNode *Node
	// middleware
	handlers []HandlerFunc
	index    int

	value map[string]any //附加值

	err error

	ctx context.Context
}

const exitIndex = math.MaxInt8

type Response struct {
	Header     http.Header
	StatusCode int
}

func DefaultContext(server *Engine) *Context {
	return &Context{
		server:     server,
		StatusCode: 200,
		handlers:   []HandlerFunc{},
		index:      -1,
		ctx:        context.Background(),
	}
}

func newContext(server *Engine, w http.ResponseWriter, req *http.Request) *Context {
	ctx := DefaultContext(server)
	ctx.Path = req.URL.Path
	ctx.Method = req.Method
	ctx.Request = req
	ctx.writer = w
	ctx.RespHeader = http.Header{}
	ctx.BodyBuffer = bufferpool.GetBuffer()
	ctx.value = nil

	return ctx
}

func (c *Context) Reset(writer http.ResponseWriter, req *http.Request) {
	c.index = -1
	c.StatusCode = 200
	c.handlers = []HandlerFunc{}

	c.Request = req
	c.Path = req.URL.Path
	c.Method = req.Method
	c.writer = writer
	c.responseUpdated = false
	c.reqCopyBody = nil
	c.Response = nil

	c.RespHeader = http.Header{}
	c.BodyBuffer = bufferpool.GetBuffer()
	c.value = nil
	c.err = nil
	c.ctx = context.Background()

}

func (c *Context) PostForm(key string) string {
	return c.Request.FormValue(key)
}

func (c *Context) Query(key string) string {
	return c.Request.URL.Query().Get(key)
}

func (c *Context) Status(code int) {
	if c.Response != nil {
		c.StatusCode = c.Response.StatusCode
	}
	c.StatusCode = code
}

func (c *Context) SetHeader(key string, value string) {
	c.RespHeader.Set(key, value)
}

func (c *Context) String(code int, format string, values ...interface{}) {
	c.SetHeader("Content-Type", "text/plain;charset=utf-8")
	c.Status(code)
	c.Write([]byte(fmt.Sprintf(format, values...)))
}
func (c *Context) Write(data []byte) {
	c.BodyBuffer.Write(data)
}
func (c *Context) WriteString(data string) {
	c.BodyBuffer.Write(conv.String2Bytes(data))
}

func (c *Context) WriteJSON(obj interface{}, code ...int) {
	c.SetHeader("Content-Type", "application/json")
	if len(code) > 0 {
		c.Status(code[0])
	}

	encoder := jsoniter.NewEncoder(c.BodyBuffer)
	if err := encoder.Encode(obj); err != nil {
		http.Error(c.writer, err.Error(), 500)
	}
}

func (c *Context) Data(code int, data []byte) {
	c.Status(code)
	c.Write(data)
}

func (c *Context) HTML(code int, html string) {
	c.SetHeader("Content-Type", "text/html")
	c.Status(code)
	c.Write([]byte(html))
}

func (c *Context) RouteNodePattern() string {
	//c.routeNode 可能为空
	if c.routeNode != nil {
		return c.routeNode.pattern
	}
	return ""
}
func (c *Context) RouteNodeType() NodeTypes {
	return c.routeNode.nodeType
}
func (c *Context) Next() error {
	c.index++
	for ; c.index < len(c.handlers); c.index++ {
		err := c.handlers[c.index](c)
		if err != nil {
			c.err = err
			return err
		}
		//及时更新response
		c.updateResponseStatus()
	}
	return nil
}
func (c *Context) Exit() {
	c.index = exitIndex
}
func (c *Context) Err(err error) {
	c.err = err
	c.Exit()
}
func (c *Context) UserContext() context.Context {
	return c.ctx
}

func (c *Context) GetHttpResponseWriter() http.ResponseWriter {
	return c.writer
}

// Render 写入HttpResponseWriter
func (c *Context) Render() {
	defer c.BodyBuffer.Free()
	c.RespHeader.Set("X-Gateway", "taishan_web")
	if c.err != nil {
		content := c.err.Error()
		if strings.HasPrefix(c.RespHeader.Get("Content-Type"), "application/json") {
			content = `{"status":0,"code":1000,"msg":"` + content + `"}`
		}
		Error(c).Caller().
			Str("trace_id", c.Request.Host+c.Request.URL.String()).
			Str("content", content).
			Msg("context err")
		utils.CopyHeader(c.writer.Header(), c.RespHeader)
		//后端可能是gzip加密，更改为不加密
		c.writer.Header().Set("Content-Encoding", "no")
		c.writer.WriteHeader(c.StatusCode)
		c.writer.Write([]byte(content))

		return
	}

	// BodyBuffer.Len() == 0 说明没有修改过response.body
	// BodyBuffer.Len() != 0 说明body被修改，使用bodybuffer作为body
	if c.Response != nil && c.BodyBuffer.Len() == 0 {
		Debug(c).
			Str("trace_id", c.Request.Host+c.Request.URL.String()).
			Msg("response output")
		c.Response.StatusCode = c.StatusCode
		utils.CopyHeader(c.writer.Header(), c.RespHeader)
		c.writer.WriteHeader(c.Response.StatusCode)
		err := utils.CopyResponseToWriter(c.writer, c.Response)

		if err != nil {
			Error(c).
				Str("trace_id", c.Request.Host+c.Request.URL.String()).
				Str("context.Response", "写入异常").Msg(err.Error())
			if strings.HasPrefix(c.RespHeader.Get("Content-Type"), "application/json;") {
				c.writer.Write([]byte(`{"status":0,"code":502,"msg":"系统内部异常"}`))
			} else {
				c.writer.Write([]byte("系统内部异常"))
			}

		}
		return
	}

	Debug(c).
		Str("trace_id", c.Request.Host+c.Request.URL.String()).
		Int("status_code", c.StatusCode).Msg("bodybuffer output")
	utils.CopyHeader(c.writer.Header(), c.RespHeader)
	c.writer.WriteHeader(c.StatusCode)
	c.writer.Write(c.BodyBuffer.Bytes())
	c.BodyBuffer.Reset()
}

func (c *Context) updateResponseStatus() {
	if c.Response != nil && !c.responseUpdated {
		c.responseUpdated = true
		c.StatusCode = c.Response.StatusCode
		utils.CopyHeader(c.RespHeader, c.Response.Header)
		//移除Content-Length
		c.RespHeader.Del("Content-Length")
	}

}

func (c *Context) SetValue(key string, value any) {
	if c.value == nil {
		c.value = make(map[string]any)
	}
	c.value[key] = value
}
func (c *Context) GetValue(key string) any {
	if c.value == nil {
		return nil
	}
	return c.value[key]
}
func (c *Context) GetValueBool(key string) bool {
	if c.value == nil {
		return false
	}
	return cast.ToBool(c.value[key])
}
func (c *Context) GetValueInt(key string) int {
	if c.value == nil {
		return 0
	}
	return cast.ToInt(c.value[key])
}
func (c *Context) GetValueInt64(key string) int64 {
	if c.value == nil {
		return 0
	}
	return cast.ToInt64(c.value[key])
}
func (c *Context) GetValueString(key string) string {
	if c.value == nil {
		return ""
	}
	return cast.ToString(c.value[key])
}

func (c *Context) RespBody() []byte {
	if c.BodyBuffer.Len() > 0 {
		return c.BodyBuffer.Bytes()
	}
	if c.Response != nil {
		body, _ := io.ReadAll(c.Response.Body)
		c.BodyBuffer.Write(body)
		return body
	}
	return []byte{}
}

// Copy 深度复制Context对象
func (c *Context) Copy() *Context {
	ctx := c.server.pool.Get().(*Context)
	ctx.Reset(c.writer, c.Request)
	ctx.handlers = c.handlers
	ctx.index = c.index
	ctx.value = c.value
	return ctx
}
func (c *Context) PutCopyToPool() {
	c.server.pool.Put(c)
}
func (c *Context) ReqBody() []byte {
	if c.Request.Method != "POST" {
		return nil
	}
	if c.reqCopyBody != nil {
		return c.reqCopyBody
	}
	body, err := io.ReadAll(c.Request.Body) // 读取 request body 的内容
	if err != nil {
		//body = []byte("failed to get request body")
		body = nil
	}
	//todo
	//bytes.Buffer可以进行pool，避免大量内存消耗
	c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
	c.reqCopyBody = body
	return body
}
func (c *Context) RewriteReqBody(body []byte) {
	c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
}

func (c *Context) GetQueryValues() map[string][]byte {
	var queryMap map[string][]byte
	queryCache := c.GetValue("req_query_map_cache")
	if queryCache == nil {
		queryMap = ctx_parser.GetQueryMapBytes(conv.String2Bytes(c.Request.URL.RawQuery))
		c.SetValue("req_query_map_cache", queryMap)
	}
	return queryMap
}

func (c *Context) GetQueryValue(key string) []byte {
	return c.GetQueryValues()[key]

}
func (c *Context) GetQueryString(key string) string {
	return cast.ToString(c.GetQueryValue(key))
}
func (c *Context) GetQueryInt(key string) int {
	data := c.GetQueryValue(key)
	return cast.ToInt(string(data))
}
func (c *Context) GetQueryInt64(key string) int64 {
	return cast.ToInt64(c.GetQueryValue(key))
}
func (c *Context) GetQueryBool(key string) bool {
	return cast.ToBool(c.GetQueryValue(key))
}
func (c *Context) GetBodyValues() map[string][]byte {
	var dataMap map[string][]byte
	cache := c.GetValue("req_body_map_cache")
	if cache == nil {
		body := c.ReqBody()
		dataMap = ctx_parser.GetQueryMapBytes(body)
		c.SetValue("req_query_map_cache", dataMap)
	}
	return dataMap
}
func (c *Context) GetBodyValue(key string) []byte {
	return c.GetBodyValues()[key]
}
func (c *Context) GetBodyString(key string) string {
	return cast.ToString(c.GetBodyValue(key))
}
func (c *Context) GetBodyInt(key string) int {
	data := c.GetBodyValue(key)
	return cast.ToInt(string(data))
}
func (c *Context) GetBodyInt64(key string) int64 {
	return cast.ToInt64(c.GetBodyValue(key))
}
func (c *Context) GetBodyBool(key string) bool {
	return cast.ToBool(c.GetBodyValue(key))
}

func (c *Context) ExtendHandles(handles []HandlerFunc) {
	if c.handlers == nil {
		c.handlers = handles
	} else {
		c.handlers = append(c.handlers, handles...)
	}
}
